//
// Created by fairy on 2025/3/3 12:14.
//
#ifndef UI_H
#define UI_H
#include <algorithm>
#include<zq_conf.h>
#include<math.h>

#include "zq_gui.h"

// 常量声明
// 纵轴左侧起始像素
#define AXIS_LEFT  15
// 横轴底部起始像素
#define AXIS_BOTTOM  63
// 绘图区域宽高
#define PLOT_WIDTH  100
#define PLOT_HEIGHT  48
// 频谱大小
#define FFT_SIZE  128
#define HALF_FFT_SIZE (FFT_SIZE/2)
// 采样率 (单位: Hz)
#define SAMPLE_RATE 16000
#define FREQ_RESOLUTION (SAMPLE_RATE / FFT_SIZE)



#define Zoomrata 1
#define MAX_VALUE 4095
#define MAX_FREQ 24000.0f


namespace GUI
{
    class SpectrumDisplay
    {
    public:
        static void Init()
        {
            startIndex = 0;
            endIndex = HALF_FFT_SIZE;
            minAmpl = 0.0f;
            maxAmpl = 1.0f;
        }

        static void DrawAxes()
        {
            DrawHorizontalAxis();
            DrawVerticalAxis();
        }

        /**
         * 缩放幅值
         * @tparam zoomIn
         */
        template<const bool zoomIn>
        static void ZoomAmplitude()
        {
            const float range = maxAmpl - minAmpl;
            const float newRange = zoomIn ? range * 0.8f : range * 1.25f; // 缩放比例

            // 直接调整 minAmpl 和 maxAmpl
            maxAmpl = minAmpl + newRange;

            // 边界检查
            if (maxAmpl > MAX_VALUE * 2.5) maxAmpl = MAX_VALUE * 2.5;
            if (minAmpl < 0) minAmpl = 0;
        }

        /**
         * 缩放频率轴
         * @tparam zoomIn 缩放，true表示放大，false表示缩小
         */
        template<const bool zoomIn>
        static void SetZoom()
        {
            // 计算当前窗口参数（使用32位整数防止运算溢出）
            const int32_t currentStart = startIndex;
            const int32_t currentEnd = endIndex;
            const int32_t currentWidth = currentEnd - currentStart;
            const int32_t currentCenter = (currentStart + currentEnd) >> 1;

            // 计算缩放比例（整数运算避免浮点误差）
            int32_t newWidth;
            if (zoomIn)
            {
                newWidth = (currentWidth * 4) / 5; // 等效0.8倍
            } else
            {
                newWidth = (currentWidth * 5) / 4; // 等效1.25倍
                if (newWidth == currentWidth) newWidth++; // 强制至少+1
            }

            // 约束宽度范围（同时处理最大/最小限制）
            newWidth = std::max(newWidth, static_cast<int32_t>(1));
            newWidth = std::min(newWidth, static_cast<int32_t>(HALF_FFT_SIZE));

            // 计算新窗口范围（保持中心稳定性）
            int32_t newStart = currentCenter - (newWidth / 2);
            int32_t newEnd = newStart + newWidth;

            // 三阶段边界约束 -------------------------------------------------
            // 阶段1：初步平移调整
            if (newStart < 0)
            {
                newEnd += -newStart; // 向右平移溢出量
                newStart = 0;
            } else if (newEnd > HALF_FFT_SIZE)
            {
                newStart -= (newEnd - HALF_FFT_SIZE); // 向左平移溢出量
                newEnd = HALF_FFT_SIZE;
            }

            // 阶段2：硬性截断
            newStart = std::max(newStart, static_cast<int32_t>(0));
            newEnd = std::min(newEnd, static_cast<int32_t>(HALF_FFT_SIZE));

            // 阶段3：最终有效性验证
            if (newEnd <= newStart)
            {
                // 窗口完全失效时的恢复策略
                if (zoomIn)
                {
                    // 放大失败时回退到最小窗口
                    newStart = std::max(static_cast<uint16_t>(currentCenter - 1), static_cast<uint16_t>(0));
                    newEnd = std::min(static_cast<uint16_t>(newStart + 2), static_cast<uint16_t>(HALF_FFT_SIZE));
                } else
                {
                    // 缩小失败时恢复到全频谱
                    newStart = 0;
                    newEnd = HALF_FFT_SIZE;
                }
            }

            // 最终写入（转换回16位类型前验证）
            startIndex = static_cast<int16_t>(std::max(static_cast<uint16_t>(newStart), static_cast<uint16_t>(0)));
            endIndex = static_cast<int16_t>(std::min(static_cast<uint16_t>(newEnd), static_cast<uint16_t>(HALF_FFT_SIZE)));

            // 终极保护：确保至少存在一个采样点
            if (endIndex <= startIndex)
            {
                startIndex = 0;
                endIndex = std::min(1, HALF_FFT_SIZE);
            }
        }

        // 移动频谱
        // 移动频谱（整数运算优化版）
        static void Pan(const bool moveLeft)
        {
            const int16_t currentRange = endIndex - startIndex;
            if (currentRange <= 0) return; // 无效窗口直接返回

            // 计算移动步长（至少移动1个频点）
            const int16_t shift = std::max(static_cast<int16_t>(currentRange / 10), static_cast<int16_t>(1));

            if (moveLeft)
            {
                startIndex = std::max(0, startIndex - shift);
                endIndex = startIndex + currentRange;

                // 右边界二次保护
                if (endIndex > HALF_FFT_SIZE)
                {
                    endIndex = HALF_FFT_SIZE;
                    startIndex = endIndex - currentRange;
                }
            } else
            {
                endIndex = std::min(HALF_FFT_SIZE, endIndex + shift);
                startIndex = endIndex - currentRange;

                // 左边界二次保护
                if (startIndex < 0)
                {
                    startIndex = 0;
                    endIndex = startIndex + currentRange;
                }
            }

            // 最终硬性约束（应对极端情况）
            startIndex = std::max(startIndex, static_cast<int16_t>(0));
            endIndex = std::min(endIndex, static_cast<int16_t>((HALF_FFT_SIZE)));
            if (endIndex <= startIndex)
            {
                startIndex = 0;
                endIndex = std::min(1, HALF_FFT_SIZE);
            }
        }

        // 当段长小于1时，同一个FFT数据对应多个像素点，会导致尖峰不存在。为了解决这个问题，不能按照横轴像素点为单位绘制了
        static void DrawSpectrum(const float spectrum[])
        {
            const uint16_t width = endIndex - startIndex;
            const float max_minus_min = maxAmpl - minAmpl;
            const float scaleFactor = 1.0f / max_minus_min; // 假设max_minus_min不为零
            const int16_t y_min = static_cast<int16_t>(AXIS_BOTTOM - 2 * PLOT_HEIGHT);
            const int16_t y_max = static_cast<int16_t>(AXIS_BOTTOM - AXIS_LEFT);
            const int16_t axis_bottom_minus_left = AXIS_BOTTOM - AXIS_LEFT;

            uint16_t prevY = AXIS_BOTTOM;

            if (width > PLOT_WIDTH)
            {
                const float sectionLength = static_cast<float>(width) / PLOT_WIDTH;
                uint16_t current_x = AXIS_LEFT;

                for (uint16_t i = 0; i < PLOT_WIDTH; ++i, ++current_x)
                {
                    const int16_t startIdx = startIndex + static_cast<int16_t>(sectionLength * i);
                    const int16_t endIdx = std::min(static_cast<int16_t>(startIdx + sectionLength),
                                                    static_cast<int16_t>(HALF_FFT_SIZE - 1));

                    // 使用指针遍历查找最大值
                    float maxVal = 0.0f;
                    const float *p = spectrum + startIdx;
                    const float *const pEnd = spectrum + endIdx;
                    for (; p <= pEnd; ++p)
                    {
                        if (*p > maxVal)
                            maxVal = *p;
                    }

                    // 计算Y坐标并约束范围
                    int16_t y = axis_bottom_minus_left - static_cast<int>(
                                    (maxVal - minAmpl) * scaleFactor * PLOT_HEIGHT);
                    y = std::max(y_min, std::min(y, y_max));
                    y = y < 0 ? 0 : y; // 最终越界保护

                    // 绘制连线
                    if (i > 0)
                    {
                        Tools::draw_line(current_x - 1, prevY, current_x, y);
                    }
                    prevY = y;
                }
            } else
            {
                const float sectionLength = static_cast<float>(PLOT_WIDTH) / width;
                uint16_t x_prev = AXIS_LEFT;
                float x_current = AXIS_LEFT;

                for (uint16_t i = 0; i < width; ++i)
                {
                    // 计算Y坐标
                    const float val = spectrum[startIndex + i];
                    int16_t y = axis_bottom_minus_left - static_cast<int>((val - minAmpl) * scaleFactor * PLOT_HEIGHT);
                    y = std::max(y_min, std::min(y, y_max));
                    y = y < 0 ? 0 : y;

                    // 计算X坐标并绘制
                    x_current = AXIS_LEFT + i * sectionLength;
                    const uint16_t x = static_cast<uint16_t>(x_current);
                    if (i > 0)
                    {
                        Tools::draw_line(x_prev, prevY, x, y);
                    }
                    x_prev = x;
                    prevY = y;
                }
            }
        }


        // 设置幅度范围
        static void SetAmplitudeRange(const float min, const float max)
        {
            minAmpl = min;
            maxAmpl = max;
        }

    private:
        static int16_t startIndex; // FFT数组抽取范围内的起始索引
        static int16_t endIndex; // FFT数组抽取范围内的截止索引
        static float minAmpl;
        static float maxAmpl;


        static void DrawHorizontalAxis()
        {
            // 绘制横轴
            Tools::draw_hline(AXIS_LEFT, PLOT_WIDTH + AXIS_LEFT, PLOT_HEIGHT);

            // 把原FFT数组分成若干段后，每段的长度（向上取整）
            const int16_t sectionLength = ((endIndex - startIndex) + PLOT_WIDTH - 1) / PLOT_WIDTH;

            // 主要刻度（每20像素）
            uint16_t freqStr_prev = 0;
            for (uint16_t x = AXIS_LEFT; x <= AXIS_LEFT + PLOT_WIDTH; x += 20)
            {
                Tools::draw_vline(x, AXIS_BOTTOM - AXIS_LEFT - 2, AXIS_BOTTOM - AXIS_LEFT);

                const int16_t IdxWidth = endIndex - startIndex;
                const float rate = static_cast<float>(x - AXIS_LEFT) * sectionLength / PLOT_WIDTH;
                const float freq = (startIndex + IdxWidth * rate) * FREQ_RESOLUTION; // 换算成频率

                char buf[8];
                const uint16_t freqStr = freq / 1000;
                if (freqStr == freqStr_prev && x != AXIS_LEFT)continue; // 避免重复标签
                freqStr_prev = freqStr;
                std::sprintf(buf, "%d", freqStr);

                // 调整标签位置防止溢出
                uint16_t xPos = x;
                if (x < AXIS_LEFT + 10) xPos = AXIS_LEFT;
                else if (x > AXIS_LEFT + PLOT_WIDTH) xPos = AXIS_LEFT + PLOT_WIDTH;

                Tools::draw_string(buf, xPos, AXIS_BOTTOM - AXIS_LEFT, Font::fonts_10x10);
            }
        }

        static void DrawVerticalAxis()
        {
            // 绘制纵轴
            Tools::draw_vline(AXIS_LEFT, 0, PLOT_HEIGHT);
            uint16_t amplStr_prev = 0;
            // 主要刻度（每10像素）
            for (uint16_t y = AXIS_BOTTOM - PLOT_HEIGHT; y <= AXIS_BOTTOM; y += 12)
            {
                Tools::draw_hline(AXIS_LEFT, AXIS_LEFT + 3, y - AXIS_LEFT - 8);

                // 计算当前刻度对应的幅值
                const float ampl = minAmpl + (maxAmpl - minAmpl) *(AXIS_BOTTOM - y) / static_cast<float>(PLOT_HEIGHT);
                const uint16_t amplStr = static_cast<uint16_t>(ampl);
                char buf[6];
                std::sprintf(buf, "%d", amplStr); // 显示整数刻度
                if (amplStr ==amplStr_prev)continue;
                amplStr_prev = amplStr;

                // 调整标签位置
                uint16_t xPos = AXIS_LEFT - 10;
                if (ampl >= 10.0f) xPos -= 5;
                Tools::draw_string(buf, xPos, y - AXIS_LEFT, Font::fonts_10x10);
            }
        }
    };
}

#undef SAMPLE_RATE

#endif //UI_H
